﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedMember.Global

/* MessageId.cs -- Message-ID
 * Ars Magna project, http://arsmagna.ru
 */

using System;
using System.ComponentModel;
using System.IO;
using System.Text.Json.Serialization;
using System.Xml.Serialization;

using AM;
using AM.IO;
using AM.Runtime;
using AM.Text;

#nullable enable

namespace ManagedIrbis.Identifiers
{
    //
    // https://en.wikipedia.org/wiki/Message-ID
    //
    // Message-ID is a unique identifier for a digital message,
    // most commonly a globally unique identifier used in email
    // and Usenet newsgroups.
    //
    // Message-IDs are required to have a specific format which
    // is a subset of an email address and to be globally unique.
    // That is, no two different messages must ever have the same
    // Message-ID. A common technique used by many message systems
    // is to use a time and date stamp along with the local host's
    // domain name, e.g., 950124.162336@example.com. On the other
    // hand, if two messages have the same Message-ID, they are presumed
    // to be the same one and one version is discarded. But, as Google
    // MTAs (message transfer agents) mangle the ones of Outlook,
    // several same messages may coexist with original and mangled
    // ones and create problems in referencing each other for threading.
    //
    // Message-IDs, if present, are generated by the client program
    // sending the email (mail user agent, or MUA) or by the first
    // mail server (mail submission agent, or MSA).
    //
    // Mails to public mailing-lists and news groups can be found
    // via their Message-ID by using links from various services:
    //
    // http://mid.gmane.org/alpine.BSF.2.00.1004291655110.61392@x.fncre.vasb
    // http://news.gmane.org/find-root.php?message_id=alpine.BSF.2.00.1004291655110.61392@x.fncre.vasb
    // https://marc.info/?i=Prayer.1.0.18.0811191202370.4107@sabinus.cs.umn.edu
    // http://mid.mail-archive.com/9dc4201d0807272359r1f0ad9d6ta44f31f439de58b7@mail.gmail.com
    // https://www.freebsd.org/cgi/mid.cgi?db=mid&id=199802242058.MAA24843@monk.via.net
    // https://lists.debian.org/msgid-search/20050606213954.GC884@finlandia.infodrom.north.de
    // http://lists.debconf.org/cgi-lurker/keyword.cgi?doc-url=/lurker&format=en.html&query=id:87vey4ogcj.fsf@vorlon.ganneff.de
    // https://www.w3.org/mid/alpine.BSF.2.00.1004291655110.61392@x.fncre.vasb
    // http://www.postgresql.org/message-id/20060419051850.96025.qmail@web31802.mail.mud.yahoo.com
    // http://public-inbox.org/git/Pine.LNX.4.58.0504150753440.7211@ppc970.osdl.org
    // http://article.olduse.net/40478%40ucbvax.BERKELEY.EDU
    // http://al.howardknight.net/msgid.cgi?STYPE=msgid&A=0&MSGI=<usenet-hier-faq-1516003264%246620%40hope.eyrie.org>
    //
    // See also:
    // RFC 2392 Content-ID and Message-ID Uniform Resource Locators
    // RFC 2111 Content-ID and Message-ID Uniform Resource Locators (Obsolete)
    // RFC 5322 Internet Message Format
    //

    /// <summary>
    /// Message-ID.
    /// </summary>
    [XmlRoot ("message")]
    public sealed class MessageId
        : IHandmadeSerializable,
        IVerifiable
    {
        #region Properties

        /// <summary>
        /// Идентификатор.
        /// </summary>
        [XmlElement ("identifier")]
        [JsonPropertyName ("identifier")]
        [Description ("Идентификатор")]
        [DisplayName ("Идентификатор")]
        public string? Identifier { get; set; }

        /// <summary>
        /// Произвольные пользовательские данные.
        /// </summary>
        [XmlIgnore]
        [JsonIgnore]
        [Browsable (false)]
        public object? UserData { get; set; }

        #endregion

        #region Public methods

        /// <summary>
        /// Разбор текстового представления.
        /// </summary>
        public void ParseText
            (
                ReadOnlySpan<char> text
            )
        {
            Sure.NotEmpty (text);

            var navigator = new ValueTextNavigator(text);
            Identifier = navigator.GetRemainingText().Trim().ThrowIfEmpty().ToString();
        }

        #endregion

        #region IHandmadeSerializable members

        /// <inheritdoc cref="IHandmadeSerializable.RestoreFromStream"/>
        public void RestoreFromStream
            (
                BinaryReader reader
            )
        {
            Sure.NotNull (reader);

            Identifier = reader.ReadNullableString();
        }

        /// <inheritdoc cref="IHandmadeSerializable.SaveToStream"/>
        public void SaveToStream
            (
                BinaryWriter writer
            )
        {
            Sure.NotNull (writer);

            writer
                .WriteNullable (Identifier);
        }

        #endregion

        #region IVerifiable members

        /// <inheritdoc cref="IVerifiable.Verify"/>
        public bool Verify
            (
                bool throwOnError
            )
        {
            var verifier = new Verifier<MessageId> (this, throwOnError);

            verifier
                .NotNullNorEmpty (Identifier);

            return verifier.Result;
        }

        #endregion

        #region Object members

        /// <inheritdoc cref="object.ToString"/>
        public override string ToString()
        {
            return Identifier.ToVisibleString();
        }

        #endregion
    }
}
